package com.gf.framework.mongo.controller;

import com.gf.framework.common.exception.BizException;
import com.gf.framework.common.model.Page;
import com.gf.framework.common.model.SimpleConditionDto;
import com.gf.framework.common.model.SimplePageDto;
import com.gf.framework.common.model.SimpleUpdateDto;
import com.gf.framework.common.utils.map.GfHashMap;
import com.gf.framework.common.utils.map.KVConsumer;
import com.gf.framework.mongo.entity.BaseEntity;
import com.gf.framework.mongo.service.GfMongoTemplate;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.BiConsumer;

/**
 * @author gaofei
 * @date 2022/6/6 18:55
 */
public abstract class MongoCrudController<T extends BaseEntity, TEMP extends GfMongoTemplate<T>> implements SmartInitializingSingleton {
    @Autowired
    protected TEMP gfMongoTemplate;

    @Autowired
    protected MongoTemplate mongoTemplate;

    private final GfHashMap<String, Field> fieldHashMap = new GfHashMap<>();

    @ApiOperation("新增或保存数据，成功返回保存的id")
    @PostMapping("save")
    public Long save(@RequestBody T data) {
        data = gfMongoTemplate.save(data);
        if (data != null) {
            return data.getId();
        }
        return null;
    }

    @ApiOperation("通过id更新数据")
    @PostMapping("updatebyid")
    public T updateById(@RequestBody T data) {
        return gfMongoTemplate.save(data);
    }

    @ApiOperation("通过id删除")
    @DeleteMapping("delbyid/{id}")
    public Boolean deleteById(@PathVariable("id")Long id) {
        DeleteResult deleteResult = gfMongoTemplate.removeById(id);
        return deleteResult.getDeletedCount() > 0;
    }

    @ApiOperation("根据id获取")
    @GetMapping("getbyid/{id}")
    public T queryById(@PathVariable("id")Long id) {
        return gfMongoTemplate.lambdaQuery(BaseEntity::getId).is(id).findOne();
    }

    @ApiOperation("根据简单条件查询")
    @PostMapping("simplequery")
    public Page<T> simplequery(@RequestBody SimplePageDto simplePageDto) throws NoSuchFieldException {
        Sort sort = sortStructure(simplePageDto.getOrder());

        Query query = queryStructure(simplePageDto);
        if (sort != null) {
            query.with(sort);
        }
        long count = mongoTemplate.count(query, gfMongoTemplate.getCollectionName());
        query.skip((long) (simplePageDto.getPageIndex() - 1) * simplePageDto.getLength()).limit(simplePageDto.getLength());

        List<T> data = mongoTemplate.find(query, gfMongoTemplate.getEntityClass(), gfMongoTemplate.getCollectionName());
        Page<T> page = new Page<>(simplePageDto.getPageIndex(), simplePageDto.getLength(), count);
        page.setRecords(data);
        return page;
    }

    @ApiOperation("根据条件更新一个符合的")
    @PostMapping("updateOne")
    public Boolean updateOne(@RequestBody SimpleUpdateDto simpleUpdateDto) throws NoSuchFieldException {
        Update update = new Update();
        for (Map.Entry<String, String> entry : simpleUpdateDto.getUpdate().entrySet()) {
            update.set(entry.getKey(), toObj(entry.getKey(), entry.getValue()));
        }
        UpdateResult updateResult = mongoTemplate.updateFirst(
                queryStructure(simpleUpdateDto),
                update, gfMongoTemplate.getCollectionName());
        return updateResult.getModifiedCount() > 0;
    }

    @ApiOperation("根据条件更新所有")
    @PostMapping("updateAll")
    public Long updateAll(@RequestBody SimpleUpdateDto simpleUpdateDto) throws NoSuchFieldException {
        Update update = new Update();
        for (Map.Entry<String, String> entry : simpleUpdateDto.getUpdate().entrySet()) {
            update.set(entry.getKey(), toObj(entry.getKey(), entry.getValue()));
        }
        UpdateResult updateResult = mongoTemplate.updateMulti(
                queryStructure(simpleUpdateDto),
                update, gfMongoTemplate.getCollectionName());
        return updateResult.getModifiedCount();
    }

    @Data
    @NoArgsConstructor
    private static class ColumCondition{
        private List<String> in;
        private String eq;
        private String lt;
        private String lte;
        private String gt;
        private String gte;
    }

    private Map<String, ColumCondition> columConditionStructure(SimpleConditionDto conditionDto) {
        Map<String, ColumCondition> ret = new HashMap<>();

        if (conditionDto.getIn() != null) {
            for (String colum : conditionDto.getIn().keySet()) {
                ret.computeIfAbsent(colum, k-> new ColumCondition()).setIn(conditionDto.getIn().get(colum));
            }
        }
        if (conditionDto.getEq() != null) {
            for (String colum : conditionDto.getEq().keySet()) {
                ret.computeIfAbsent(colum, k-> new ColumCondition()).setEq(conditionDto.getEq().get(colum));
            }
        }
        if (conditionDto.getLt() != null) {
            for (String colum : conditionDto.getLt().keySet()) {
                ret.computeIfAbsent(colum, k-> new ColumCondition()).setLt(conditionDto.getLt().get(colum));
            }
        }
        if (conditionDto.getLte() != null) {
            for (String colum : conditionDto.getLte().keySet()) {
                ret.computeIfAbsent(colum, k-> new ColumCondition()).setLte(conditionDto.getLte().get(colum));
            }
        }
        if (conditionDto.getGt() != null) {
            for (String colum : conditionDto.getGt().keySet()) {
                ret.computeIfAbsent(colum, k-> new ColumCondition()).setGt(conditionDto.getGt().get(colum));
            }
        }
        if (conditionDto.getGte() != null) {
            for (String colum : conditionDto.getGte().keySet()) {
                ret.computeIfAbsent(colum, k-> new ColumCondition()).setGte(conditionDto.getGte().get(colum));
            }
        }
        return ret;
    }

    /**
     * 构建查询条件
     * @return
     */
    private Query queryStructure(SimpleConditionDto conditionDto) throws NoSuchFieldException {
        // 构建条件
        Map<String, ColumCondition> columOperation = columConditionStructure(conditionDto);

        Criteria finalCriteria = null;

        if (!columOperation.isEmpty()) {
            for (Map.Entry<String, ColumCondition> entry : columOperation.entrySet()) {
                String colum = entry.getKey();
                ColumCondition columCondition = entry.getValue();
                if (finalCriteria == null) {
                    finalCriteria = new Criteria(colum);
                }
                else {
                    finalCriteria = finalCriteria.and(colum);
                }
                if (columCondition.getIn() != null && !columCondition.getIn().isEmpty()) {
                    List<Object> objects = new ArrayList<>();
                    for (String opVvalue : columCondition.getIn()) {
                        objects.add(toObj(colum, opVvalue));
                    }
                    finalCriteria.in(objects);
                }
                if (!StringUtils.isBlank(columCondition.getEq())) {
                    finalCriteria.is(toObj(colum, columCondition.getEq()));
                }
                if (!StringUtils.isBlank(columCondition.getLt())) {
                    finalCriteria.lt(toObj(colum, columCondition.getLt()));
                }
                if (!StringUtils.isBlank(columCondition.getLte())) {
                    finalCriteria.lte(toObj(colum, columCondition.getLte()));
                }
                if (!StringUtils.isBlank(columCondition.getGt())) {
                    finalCriteria.gt(toObj(colum, columCondition.getGt()));
                }
                if (!StringUtils.isBlank(columCondition.getGte())) {
                    finalCriteria.gte(toObj(colum, columCondition.getGte()));
                }
            }
        }
        return finalCriteria == null ? new Query() : new Query(finalCriteria);
    }

    /**
     * 构建排序条件
     * @param sort
     * @return
     */
    private Sort sortStructure(String sort) {
        Sort ret = null;
        if (sort != null && !sort.isEmpty()) {
            String[] sorts = sort.split("#");
            for (String sortStr : sorts) {
                String[] sortInfo = sortStr.split(":");
                if ("asc".equalsIgnoreCase(sortInfo[0])) {
                    if (ret == null) {
                        ret = Sort.by(Sort.Direction.ASC, sortInfo[1]);
                    }
                    else {
                        ret = ret.and(Sort.by(Sort.Direction.ASC, sortInfo[1]));
                    }
                }
                else if ("desc".equalsIgnoreCase(sortInfo[0])) {
                    if (ret == null) {
                        ret = Sort.by(Sort.Direction.DESC, sortInfo[1]);
                    }
                    else {
                        ret = ret.and(Sort.by(Sort.Direction.DESC, sortInfo[1]));
                    }
                }
            }
        }
        return ret;
    }

    /**
     * 参数转换
     * @param columName
     * @param value
     * @return
     * @throws NoSuchFieldException
     */
    private Object toObj(String columName, String value) throws NoSuchFieldException {
        Field field = fieldHashMap.get(columName);
        if (field == null) {
            throw new BizException(String.format("无法映射的属性{%s}", columName));
        }
        if (field.getType().equals(Byte.class) || field.getType().equals(Short.class) || field.getType().equals(Integer.class) ||
                field.getType().equals(Long.class) || field.getType().equals(Float.class) || field.getType().equals(Double.class)) {
            return Double.parseDouble(value);
        }
        else if (field.getType().equals(String.class)) {
            return value;
        }
        else if (field.getType().equals(LocalDate.class)) {
            DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            return LocalDate.parse(value, format);
        }
        else if (field.getType().equals(LocalDateTime.class)) {
            DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            return LocalDateTime.parse(value, format);
        }
        else if (field.getType().equals(Boolean.class)) {
            return Boolean.parseBoolean(value);
        }
        throw new BizException("不支持的条件类型");
    }

    @Override
    public void afterSingletonsInstantiated() {
        Class<?> classzz = gfMongoTemplate.getEntityClass();
        while (classzz.getSuperclass() != null) {
            Field[] fields = classzz.getDeclaredFields();
            fieldHashMap.forEach((s, field) -> {

            });

            fieldHashMap.putAll(fields, new KVConsumer<String, Field, Field>() {
                @Override
                public String key(Field field) {
                    return field.getName();
                }
                @Override
                public Field value(Field field) {
                    return field;
                }
            });
            classzz = classzz.getSuperclass();
        }
    }
}
